// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System.Collections.Generic;
using System.Data;

namespace SnapObjects.Data
{
    internal abstract class DataModelBinder<TModel>
        : IDataModelBinder<TModel>
    {
        protected IModelFactory<TModel> _factory;
        protected IModelAccessor<TModel> _accessor;
        protected DataModelBinder(
            IModelFactory<TModel> factory,
            IList<DbColumnInfo> columnSchema,
            ITypeMapper typeMapper)
        {
            _factory = factory;
            _accessor = _factory.GetAccessor();

            this.ModelBinding = new DataModelBinding(columnSchema,
                        (IPocoModelMeta)_accessor.ModelMeta, typeMapper);
        }

        protected DataModelBinder(DataModelBinding binding)
        {
            this.ModelBinding = binding;
        }

        public DataModelBinding ModelBinding { get; protected set; }

        public IModelFactory<TModel> ModelFactory => _factory;

        protected abstract void SetValue(TModel model, int index, object value);

        protected abstract void SetValue(TModel model, string name, object value);

        public virtual TModel BindModel(IDataReader dataReader)
        {
            var model = _factory.Create();

            foreach (var binding in this.ModelBinding.Properties)
            {
                var value = this.GetDbValue(dataReader, binding);

                this.SetValue(model, binding.Property.Name, value);
            }

            return model;
        }

        protected object GetDbValue(IDataReader dataReader, DataPropertyBinding binding)
        {
            var dbColumnInfo = binding.DbColumnInfo;

            if (dataReader.IsDBNull(dbColumnInfo.ColumnOrdinal))
            {
                return null;
            }

            object value = dataReader.GetValue(dbColumnInfo.ColumnOrdinal);

            if (binding.Property.HasConversion)
            {
                var valueConverter = binding.Property.ValueConverter;

                return valueConverter.ConvertToProperty(value);
            }

            return binding.TypeMapping.ChangeDbType(value);
        }
    }
}
